﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using SqlScriptVault.Commands;
using SqlScriptVault.Model;
using SqlScriptVault.Services;
using System.ComponentModel;
using System.Windows;
using System.IO;

namespace SqlScriptVault.ViewModel
{
    class ScriptVaultViewModel : BaseNotifyViewModel
    {
        private readonly IFileService _fileService;
        private readonly IMessageDisplayService _messageDisplayService;

        private Script _currentScript;

        public ScriptVaultViewModel(IFileService fileService, IMessageDisplayService messageDisplayService)
        {
            _fileService = fileService;
            _messageDisplayService = messageDisplayService;

            EventService.Instance.Subscribe(EventService.NewEvent, NewFile);
            EventService.Instance.Subscribe(EventService.LoadEvent, LoadFromFile);
            EventService.Instance.Subscribe(EventService.SaveEvent, SaveToFile);
            EventService.Instance.Subscribe(EventService.ExecuteEvent, Execute);
            EventService.Instance.Subscribe(EventService.OpenHistoryEvent, OpenHistory);

            // aggregate ?
            ScriptVault = new ScriptVault();
        }

        public ScriptVaultViewModel()
            : this(new FileService(), new MessageDisplayService())
        {
        }

        ~ScriptVaultViewModel()
        {
            EventService.Instance.Unsubscribe(EventService.NewEvent);
            EventService.Instance.Unsubscribe(EventService.LoadEvent);
            EventService.Instance.Unsubscribe(EventService.SaveEvent);
            EventService.Instance.Unsubscribe(EventService.ExecuteEvent);
        }

        public string CurrentFileName { get; private set; }

        public ScriptVault ScriptVault { get; private set; }
        public Script CurrentScript
        {
            get { return _currentScript; }
            set
            {
                _currentScript = value;

                RaisePropertyChanged("CurrentScript");
            }
        }

        public RelayCommand AddFileCommand
        {
            get
            {
                return new RelayCommand(param => AddFile());
            }
        }

        public RelayCommand UpdateFileCommand
        {
            get
            {
                return new RelayCommand(param => UpdateFile());
            }
        }

        private void AddFile()
        {
            string fileName = _fileService.OpenFileDialog("*.sql", "Sql Scripts (.sql)|*.sql");

            if (fileName == null)
            {
                return;
            }

            if (!_fileService.Exists(fileName))
            {
                _messageDisplayService.ShowErrorMessage("File not found!");
                return;
            }

            bool success = false;
            Script script;

            do
            {
                script = _messageDisplayService.OpenScript(fileName);

                if (script == null)
                {
                    return;
                }

                if (ScriptVault.ScriptNameExists(script.ScriptName))
                {
                    _messageDisplayService.ShowErrorMessage("Script name already in use");
                }
                else
                {
                    success = true;
                }

            } while (!success);

            ScriptVault.Add(script);

            RaisePropertyChanged("ScriptVault");

            EventService.Instance.Publish(EventService.ScriptVaultChangedEvent, null);
        }

        private void UpdateFile()
        {
            string fileName = _fileService.OpenFileDialog("*.sql", "Sql Scripts (.sql)|*.sql");

            if (fileName == null)
            {
                return;
            }

            if (!_fileService.Exists(fileName))
            {
                _messageDisplayService.ShowErrorMessage("File not found!");
                return;
            }

            string previousScript = CurrentScript.ScriptContent;
            string nextScript = File.ReadAllText(fileName);

            CurrentScript.ScriptContent = nextScript;
            ScriptVault.AddUpdatedFileHistory(CurrentScript.ScriptName, previousScript, nextScript);

            RaisePropertyChanged("CurrentScript.ScriptContent");

            EventService.Instance.Publish(EventService.ScriptVaultChangedEvent, null);
        }

        private object NewFile(object obj)
        {
            if (ScriptVault != null && ScriptVault.HasChanges)
            {
                MessageBoxResult result = _messageDisplayService.ShowQuestion("Discard unsaved changes?", "Warning");

                if (result == MessageBoxResult.No)
                {
                    return false;
                }
            }

            ScriptVault = new ScriptVault();
            CurrentScript = null;

            RaisePropertyChanged("ScriptVault");

            return true;
        }

        private object LoadFromFile(object obj)
        {
            if (ScriptVault != null && ScriptVault.HasChanges)
            {
                MessageBoxResult result = _messageDisplayService.ShowQuestion("Discard unsaved changes?", "Warning");

                if (result == MessageBoxResult.No)
                {
                    return false;
                }
            }

            string fileName = (string)obj;

            CurrentFileName = fileName;
            ScriptVault = _fileService.LoadFile<ScriptVault>(fileName);

            RaisePropertyChanged("ScriptVault");

            return true;
        }

        private object SaveToFile(object obj)
        {
            string fileName = (string)obj;

            if (fileName != null)
            {
                CurrentFileName = fileName;

                _fileService.SaveFile<ScriptVault>(fileName, ScriptVault);
            }
            else
            {
                _fileService.SaveFile<ScriptVault>(CurrentFileName, ScriptVault);
            }

            ScriptVault.Saved();

            return null;
        }

        private object Execute(object obj)
        {
            _messageDisplayService.OpenExecution(ScriptVault);

            return null;
        }

        private object OpenHistory(object obj)
        {
            _messageDisplayService.OpenHistory(ScriptVault);

            return null;
        }
    }
}
